package com.kelvem.saas.workbanch.fsm.container;

import com.kelvem.saas.workbanch.core.utils.BeanUtil;
import com.kelvem.saas.workbanch.fsm.entity.Entity;
import com.kelvem.saas.workbanch.fsm.entity.FsmEntity;
import com.kelvem.saas.workbanch.fsm.core.FsmException;
import com.kelvem.saas.workbanch.fsm.model.EventNode;
import com.kelvem.saas.workbanch.fsm.model.FsmNode;
import com.kelvem.saas.workbanch.fsm.model.StateGroupNode;
import com.kelvem.saas.workbanch.fsm.model.StateNode;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * FsmContext
 *
 * @author kelvem
 */
@Slf4j
@Service
public class FsmContainer {

    private static Map<Class<? extends FsmEntity>, FsmNode> fsmMap = new LinkedHashMap<>();
    private static Map<String, EventNode> eventCodeMap = new HashMap<String, EventNode>();
    
    /**
     * add data
     *
     * @param entityClazz
     * @param parentEntityClazz
     * @param stateList
     * @param groupList
     * @param eventList
     * @return FsmNode
     */
    public static FsmNode addData(Class<FsmEntity> entityClazz, Class<FsmEntity> parentEntityClazz, List<StateNode> stateList, List<StateGroupNode> groupList, List<EventNode> eventList) {
        // get fsm
        FsmNode fsm = FsmContainer.getFsmNode(entityClazz);
        if (fsm == null) {
            fsm = new FsmNode(entityClazz, parentEntityClazz);
        }
        
        // ParentEntityName
        if (parentEntityClazz == null || parentEntityClazz.equals(Entity.class)) {
            // parentEntityClazz为空, 什么也不做
        } else if (fsm.getParentEntityClazz() == null) {
            fsm.setParentEntityClazz(parentEntityClazz);
        } else if (!fsm.getParentEntityClazz().equals(parentEntityClazz)) {
            String msg = String.format("%s的parentEntityClazzy拥有不同的多个值[%s, %s], 请确认",
                    entityClazz.getSimpleName(), fsm.getParentEntityName(), parentEntityClazz.getSimpleName());
            log.error(msg);
            throw new FsmException(msg);
        }
        
        // set buff info
        fsm.addData(stateList, groupList, eventList);
    
        fsmMap.put(fsm.getEntityClazz(), fsm);
        for (EventNode event :
                eventList) {
            eventCodeMap.put(event.getEntityName() + "-" + event.getCode().toLowerCase(), event);
        }
        
        return fsm;
    }
    
    /**
     * trigger event
     *
     * @param entityClazz
     * @param eventCode
     * @param req
     */
    public static void triggerEvent(Class<? extends FsmEntity> entityClazz, String eventCode, Object req) {
        log.info("trigger Event[" + eventCode + "] request=" + req);
        EventNode event = FsmContainer.getEvent(entityClazz, eventCode);
        if (event == null) {
            String msg = String.format("未找到该事件, 请确认注解, @Fsm(entityClazz=%s.class)和@RegisterEvent(eventCode=\"%\")", entityClazz.getSimpleName(), eventCode);
            log.error(msg);
            throw new FsmException(entityClazz, eventCode, msg);
        }
        Object bean = BeanUtil.getBean(event.getBeanName());
        Method method = event.getMethod();
        String methodName = event.getMethodName();
        
        try {
            log.info("trigger Event[" + eventCode + "] method=" + methodName + ", bean=" + bean);
            Object ret = ReflectionUtils.invokeMethod(method, bean, req);
            log.info("trigger Event[" + eventCode + "] ret=" + ret);
        } catch (Exception e) {
            log.error("Event[" + eventCode + "] err, " + e.getMessage());
            throw new FsmException("Event[" + eventCode + "] err, " + e.getMessage(), e);
        }
    }
    
    /**
     * 状态机 最后检查与补充信息
     */
    public static void finish() {
        
        for (FsmNode fsmNode :
                FsmContainer.getFsmMap().values()) {
            
            /**
             * state
             */
            for (StateNode state :
                    fsmNode.getStateList()) {
                // check
            }
            
            /**
             * stateGroup
             */
            for (StateGroupNode group :
                    fsmNode.getGroupStateList()) {
                // check
            }
            fsmNode.resetGroupStateNameMapping();
            
            /**
             * event
             */
            for (EventNode event :
                    fsmNode.getEventList()) {
                
                List<String> srcStateList = fsmNode.getEventStateList(event.getSrcState());
                event.setSrcStateList(srcStateList);
                if (srcStateList.size() <= 0) {
                    String msg = String.format("Clazz[%s]的@RegisterEvent(eventCode=\"%s\", srcState=\"%s\"), srcState不存在, 请检查注解",
                            event.getClazzSimpleName(), event.getCode(), event.getSrcState());
                    log.error(msg);
                    throw new FsmException(msg);
                }
                
                if (fsmNode.getGroupStateMap().containsKey(event.getDstState().toLowerCase())) {
                    String msg = String.format("Clazz[%s]的@RegisterEvent(eventCode=\"%s\", dstState=\"%s\"), dstState不能设置为状态组, 请检查注解",
                            event.getClazzSimpleName(), event.getCode(), event.getDstState());
                    log.error(msg);
                    throw new FsmException(msg);
                }
                if (!fsmNode.getStateMap().containsKey(event.getDstState().toLowerCase())) {
                    String msg = String.format("Clazz[%s]的@RegisterEvent(eventCode=\"%s\", dstState=\"%s\"), dstState不存在, 请检查注解",
                            event.getClazzSimpleName(), event.getCode(), event.getDstState());
                    log.error(msg);
                    throw new FsmException(msg);
                }
    
                eventCodeMap.put(event.getEntityName() + "-" + event.getCode().toLowerCase(), event);
            }
        }
    
//        Map<Class<? extends Entity>, EntityMapppingNode> mappingMap = EntityMapppingContainer.getEntityMap();
//        Map<Class<? extends Entity>, Class<? extends Entity>> parentMap = new HashMap<>();
//        for (EntityMapppingNode mapping :
//                mappingMap.values()) {
//            parentMap.put(mapping.getLeftClazz(), mapping.getRightClazz());
//        }
//
//        Map<Class<? extends Entity>, Node> nodeMap = new HashMap<>();
//        for (FsmNode fsmNode :
//                FsmContainer.getFsmMap().values()) {
//            Node node = new Node(fsmNode);
//            nodeMap.put(fsmNode.getEntityClazz(), node);
//        }
//
//        Set<Node> entityTree = new HashSet<>();
//        for (Class<? extends Entity> clazz :
//                nodeMap.keySet()) {
//            Node node = nodeMap.get(clazz);
//            if (parentMap.containsKey(clazz)) {
//                Class<? extends Entity> parentClazz = parentMap.get(clazz);
//                Node parentNode = nodeMap.get(parentClazz);
//                parentNode.subList.add(node);
//            } else {
//                entityTree.add(node);
//            }
//        }
//
//
//        log.info(entityTree + "");
        
        
        
    }

    @Data
    static class Node {
        public Node(FsmNode node) {
            this.val = node;
        }
        private FsmNode val;
        private List<Node> subList = new ArrayList<>();
    }
    
    /**
     * print log
     */
    public static void printLog() {
        for (FsmNode fsmNode :
                FsmContainer.getFsmMap().values()) {
            
            // 打印状态机信息
            fsmNode.printLog();
        }
    }
    
    //-----------------------------------------------------------
    //        get / set / contain
    //-----------------------------------------------------------
    
    /**
     * 获取FsmNode
     *
     * @param entityClazz
     * @return FsmNode
     * @throws
     */
    public static FsmNode getFsmNode(Class<? extends Entity> entityClazz) {
        return fsmMap.get(entityClazz);
    }
    
    /**
     * 获取FsmMap
     *
     * @return FsmNode
     * @throws
     */
    public static Map<Class<? extends FsmEntity>, FsmNode> getFsmMap() {
        return fsmMap;
    }
    
    /**
     * getCurrEvent
     *
     * @param entityClazz
     * @param eventCode
     * @return EventEntity
     * @throws
     */
    public static EventNode getEvent(Class<? extends FsmEntity> entityClazz, String eventCode) {
        
        String entityName = entityClazz.getSimpleName();
        return eventCodeMap.get(entityName + "-" + eventCode.toLowerCase());
    }

}
